/**
  * @file       xunmei.c
  * @brief      讯美通讯组件
  * @copyright  Copyright (c) 2020~2030 ShenZhen Dxtc Tech. Co., Ltd.
  * All rights reserved.
  * @version    V1.0
  * @author     TianXubin
  * @date       2022-02-12 15:18:09
  * @note       鼎新同创·智能锁
  *
  *****************************************************************************/

#include "component.h"
#include "device.h"
#include "xunmei.h"

#define XM_LOG(format, ...) OSAL_LOG(C_LIGHT_PURPLE format C_NONE, ##__VA_ARGS__)
#define __XM_LOG(format, ...) __OSAL_LOG(C_LIGHT_PURPLE format C_NONE, ##__VA_ARGS__)

/* 定义虚拟硬件接口 */
#define XUNMEI_UART_VHWL vUART_4
#define XUNMEI_WAKE_PIN_VHWL vPIN_I3
#define XUNMEI_CTRL_PIN_VHWL vPIN_C8

#define MAX_PACKAGE_LEN_XM (500)           //最大包长
#define EVENT_XUNMEI_PROTOCOL (0X00000001) //协议处理事件
#define XUNMEI_PROTOCOL_PROC_CYCLE 10      //协议处理间隔时间

#define CMD_PACKET_STX 0XF5 //命令包数据头
#define ACK_PACKET_STX 0X5F //应答包数据头

#define PROTOCOL_SEND_TIMES 3           //锁发送命令包的最大次数
#define PROTOCOL_RESEND_CMD_TIMEOUT 200 //锁重发命令包间隔时间ms
#define PROTOCOL_SEND_ACK_TIME_GAP 20   //锁发出多包ACK的间隔时间ms

#define PROTOCOL_WAKE_TIMES 3            //锁发送唤醒信号的最大次数
#define PROTOCOL_RESEND_WAKE_TIMEOUT 200 //锁重发唤醒信号的间隔时间ms
#define PROTOCOL_XM_SLEEP_TIME 300       //讯美模组的休眠时间（300ms未动作就休眠了）

#define UART_REV_DATA_BUF_LEN (MAX_PACKAGE_LEN_XM * 2) //串口接收缓冲区长度

#define TX_QUEUE_LEN 30

#define XUNMEI_STARTUP_TIME 150 //讯美启动时间

static uint32_t xunmei_init_flag = 0;
__NVRAM static uint8_t xm_wake_flag = 0;
__EFRAM static uint16_t xm_ready = XUNMEI_STARTUP_TIME;

#pragma pack(1)
/* 协议头结构 */
typedef struct
{
    uint8_t stx;    //数据头
    uint16_t crc16; //校验和
    uint16_t len;   //数据长度
    uint8_t cmd;    //命令
    uint8_t psn;    //序号
} ProtocolDataHead_stu_t;

/* 命令包结构 */
typedef struct
{
    ProtocolDataHead_stu_t head;
    uint8_t dataBuf[]; //数据域长度不定
} CmdPacket_stu_t;

/* 多包数据结构体 */
typedef struct
{
    uint16_t data_len;
    uint8_t pack_id;
    uint8_t dataBuf[];
} MultiCmdPacketData_stu_t;

/* 命令包结构 */
typedef struct
{
    ProtocolDataHead_stu_t head;
    MultiCmdPacketData_stu_t data;
} MultiCmdPacket_stu_t;

/* 应答包结构 */
typedef struct
{
    ProtocolDataHead_stu_t head;
    uint8_t ret;
    uint8_t databuf[];
} AckPacket_stu_t;

/* 多包JSON缓冲区结构 */
typedef struct
{
    uint16_t len;          //数据长度
    uart_send_cb_fun_t cb; //发送命令回调（为NULL：不需要回调）
    uint8_t buffer[];      //发送缓存
} uart_txcmd_packet_stu_t;

/* 命令包发送状态信息 */
__EFRAM static struct
{
    uart_txcmd_packet_stu_t *data; //数据
    uint8_t tsn;
    uint8_t cmd;
    uint32_t timeStamp; //最后一次发送的时间戳
    uint8_t cnt;        //命令发送次数（可能重发）
    uint8_t wake_cnt;   //唤醒信号发送次数（可能重发）
    enum
    {                       //当前发送的状态
        TX_STA_IDLE,        //空闲
        TX_STA_WAIT_ACK,    //等待模块回复应答包
        TX_STA_WAIT_NOTIFY, //等待唤醒通知
    } status;
    SuperQueueHandle_t queue_tx_handle;
} CmdPacketTxInfo;

/* 命令包接收状态信息 */
__EFRAM static struct
{
    RingBufferHandle_t Rx_rbHandle;
    uint8_t tsn;
    uint32_t timeStamp; //最后一次收到数据包的时间戳
    uint8_t uart_rev_data[MAX_PACKAGE_LEN_XM];
    uint16_t received_len; //多包传输已接收长度
    uint8_t *muti_data;    //多包传输数据指针
} CmdPacketRxInfo;

#pragma pack()

/**
  * @brief  故障处理
  * @note
  * @param  err：是否故障
  * @return void
  */
static void err_process(uint8_t err)
{
    static uint8_t err_flag = 0xff;
    if (err != err_flag)
    {
        err_flag = err;
        OSAL_MessagePublishErrorCode(ERRCODE_TYPE_XUNMEI, err_flag);
    }
}

/**
  * @brief  计算CRC校验
  * @note
  * @param  *puchMsg：数据指针
  * @param  usDataLen：数据长度
  * @return uint16_t
  */
static uint16_t CRC16_XMODEM(uint8_t *puchMsg, uint32_t usDataLen)
{
    unsigned short wCRCin = 0x0000;
    unsigned short wCPoly = 0x1021;
    unsigned char wChar = 0;

    while (usDataLen--)
    {
        wChar = *(puchMsg++);
        wCRCin ^= (wChar << 8);

        for (int i = 0; i < 8; i++)
        {
            if (wCRCin & 0x8000)
            {
                wCRCin = (wCRCin << 1) ^ wCPoly;
            }
            else
            {
                wCRCin = wCRCin << 1;
            }
        }
    }
    return (wCRCin);
}

/**
  * @brief  打印HEX数据
  * @note
  * @param  *pData：数据指针
  * @param  len：数据长度
  * @param  *name：消息名字
  * @return void
  */
static void PrintfHexData(uint8_t *pData, uint16_t len, char *name)
{
    char *str_buffer = OSAL_Malloc(len * 3 + 1);
    if (str_buffer)
    {
        memset(str_buffer, 0, len * 3 + 1);
        __XM_LOG("[%ld] %s(%d): ", OSAL_GetTickCount(), name, len);
        if (str_buffer != NULL)
        {
            for (int i = 0; i < len; i++)
            {
                sprintf(&str_buffer[i * 3], "%02X ", pData[i]);
            }
            __XM_LOG("%s\r\n\r\n", str_buffer);
        }
        OSAL_Free(str_buffer);
    }
}

/**
  * @brief  唤醒信号的定时器中断回调
  * @note
  * @param  time
  * @return void
  */
static void SendWakeSignal_Callback(TimerHandle_stu_t time)
{
    (void)time;

    /* 中断IO设置成数字高阻，讯美模块有上拉电阻 */
    Device_Write(XUNMEI_WAKE_PIN_VHWL, NULL, 0, 1);
    Device_Write(XUNMEI_WAKE_PIN_VHWL, NULL, 0, PIN_MODE_INPUT_HIGHZ);
    InputPort_EnableProt("XUNMEI_WAKE");
}

/**
  * @brief  发出唤醒信号【10ms低脉冲】
  * @note
  * @param
  * @return void
  */
static int XunMei_SendWakeSignal(void)
{
    /* 中断IO设置成输出 */
    InputPort_DisableProt("XUNMEI_WAKE");
    Device_Write(XUNMEI_WAKE_PIN_VHWL, NULL, 0, PIN_MODE_OD_DRIVE);
    Device_Write(XUNMEI_WAKE_PIN_VHWL, NULL, 0, 0);
    OSAL_TimerCreate(SendWakeSignal_Callback, 10, RESET);
    return 0;
}

/**
  * @brief  加载待发送的数据
  * @note   将要发出的数据，填入发送缓存区
  *
  * @param  pData：数据指针
  * @param  len：数据长度
  * @return 成功返回true，失败返回false
  */
static ErrorStatus XunMei_SendData(uint8_t *pData, uint16_t len)
{
    if (Device_Write(XUNMEI_UART_VHWL, pData, len, 0) != 0)
    {
        return ERROR;
    }

    if (pData[5] == TX_CMD_MULTI_PACKET)
    {
        PrintfHexData(pData, sizeof(ProtocolDataHead_stu_t) + sizeof(MultiCmdPacketData_stu_t), "TX JSON-PACK");
    }
    else
    {
        PrintfHexData(pData, len, "SendData");
    }
    return SUCCESS;
}

/**
  * @brief  发布消息到应用层
  * @note
  * @param  msgType：消息类型
  * @param  cb：回调
  * @param  res：执行结果
  * @param  cmd：命令
  * @param  len：长度
  * @param  *data：数据内容
  * @return void
  */
static void XunMei_MsgPublish(XunMeiMsgType_enum_t msgType, uart_send_cb_fun_t cb, uart_send_res_enum_t res, uint8_t cmd, uint16_t len, uint8_t *data)
{
    XunMeiMsg_t *msg = (XunMeiMsg_t *)OSAL_Malloc(sizeof(XunMeiMsg_t) + len);
    if (msg)
    {
        msg->msgType = msgType;
        msg->cb = cb;
        msg->res = res;
        msg->cmd = cmd;
        msg->len = len;
        if (msg->len && data)
        {
            memcpy(msg->data, data, len);
        }
        XM_LOG("xm pub msg(0x%02x, %d)\r\n", msg->cmd, msg->len);
        OSAL_MessagePublish(msg, sizeof(XunMeiMsg_t) + len);
        OSAL_Free(msg);
    }
}

/**
  * @brief  处理超时重发
  *
  * @note   MCU发出命令包后，超时没有收到确认包，就重发
  */
static void XunMei_ProcessTimeoutResend(void)
{
    uint8_t send_timeout_msg = 0;
    uint32_t currentTime = OSAL_GetTickCount();

    if (CmdPacketTxInfo.status == TX_STA_WAIT_ACK) //等待ACK包
    {
        if (OSAL_PastTime(currentTime, CmdPacketTxInfo.timeStamp) > PROTOCOL_RESEND_CMD_TIMEOUT)
        {
            XM_LOG("wait ack timeout, cmd: 0x%02X, cnt: %d", CmdPacketTxInfo.cmd, CmdPacketTxInfo.cnt);
            if (CmdPacketTxInfo.cnt >= PROTOCOL_SEND_TIMES) // ERR:等待ACK包超时
            {
                send_timeout_msg = 1;
                CmdPacketTxInfo.status = TX_STA_IDLE;
                OSAL_SetTaskStatus(TASK_STA_NORMAL);
            }
            else
            {
                if (OSAL_PastTime(currentTime, CmdPacketRxInfo.timeStamp) < PROTOCOL_XM_SLEEP_TIME) //模组活跃
                {
                    XunMei_SendData(CmdPacketTxInfo.data->buffer, CmdPacketTxInfo.data->len); //直接重发数据包
                }
                else //不活跃
                {
                    XunMei_SendWakeSignal(); // 拉wake
                    CmdPacketTxInfo.status = TX_STA_WAIT_NOTIFY;
                    CmdPacketTxInfo.wake_cnt = 1;
                }
                CmdPacketTxInfo.timeStamp = currentTime;
                CmdPacketTxInfo.cnt++;
            }
        }
    }
    else if (CmdPacketTxInfo.status == TX_STA_WAIT_NOTIFY)
    {
        if (OSAL_PastTime(currentTime, CmdPacketTxInfo.timeStamp) > PROTOCOL_RESEND_WAKE_TIMEOUT) //等唤醒通知超时
        {
            XM_LOG("wait wake notify timeout, cnt: %d\r\n", CmdPacketTxInfo.wake_cnt);
            if (CmdPacketTxInfo.wake_cnt >= PROTOCOL_WAKE_TIMES) // ERR:等待唤醒通知超时
            {
                send_timeout_msg = 1;
                CmdPacketTxInfo.status = TX_STA_IDLE;
                OSAL_SetTaskStatus(TASK_STA_NORMAL);
                err_process(1);
            }
            else
            {
                XunMei_SendWakeSignal(); //拉wake
                CmdPacketTxInfo.timeStamp = currentTime;
                CmdPacketTxInfo.wake_cnt++;
            }
        }
    }

    /* exec timeout callback */
    if (send_timeout_msg)
    {
        XunMei_MsgPublish(XM_EXEC_CB, CmdPacketTxInfo.data->cb, UART_SEND_WAIT_ACK_TIMEOUT, CmdPacketTxInfo.cmd, 0, NULL);
        CmdPacketTxInfo.data->cb = NULL;
        CmdPacketTxInfo.cmd = 0;
        if (CmdPacketTxInfo.data)
        {
            OSAL_Free(CmdPacketTxInfo.data);
        }
    }
}

/**
  * @brief  处理接收的应答包
  * @note
  *
  * @param  pAckPacket：应答包数据指针
  */
static void ProcessRxAckPacket(AckPacket_stu_t *pAckPacket, uint16_t datalen)
{
    XM_LOG("rx ack pack: 0x%02x, psn: 0x%02x\r\n", pAckPacket->head.cmd, pAckPacket->head.psn);
    if (CmdPacketTxInfo.status == TX_STA_WAIT_ACK)
    { 
        if (pAckPacket->head.psn == CmdPacketTxInfo.tsn && pAckPacket->head.cmd == CmdPacketTxInfo.cmd)
        {
            uint8_t res = (pAckPacket->ret == XUNMEI_EXE_OK) ? UART_SEND_SUCCEED : UART_SEND_ERROR;
            XunMei_MsgPublish(XM_EXEC_CB, CmdPacketTxInfo.data->cb, res, CmdPacketTxInfo.cmd, pAckPacket->head.len - 3, pAckPacket->databuf);
            CmdPacketTxInfo.data->cb = NULL;
            CmdPacketTxInfo.status = TX_STA_IDLE;
            OSAL_SetTaskStatus(TASK_STA_NORMAL);
            CmdPacketTxInfo.cmd = 0;
            if (CmdPacketTxInfo.data)
            {
                OSAL_Free(CmdPacketTxInfo.data);
            }
        }
    }
}

/**
  * @brief  发出应答包
  * @note   MCU收到无线模组发过来命令后，通过调用该函数给模组回复应答包
  *
  * @param  cmd：命令
  * @param  res：命令处理结果
  * @param  pAckData：应答数据指针
  * @param  ackDataLen：数据长度
  */
static void SendAckPacket(uint8_t cmd, uint8_t res)
{
    uint8_t buffer[MAX_PACKAGE_LEN_XM] = {0};
    AckPacket_stu_t *pAckPacket = (AckPacket_stu_t *)buffer;

    pAckPacket->head.stx = ACK_PACKET_STX;
    pAckPacket->head.len = 3;
    pAckPacket->head.cmd = cmd;
    pAckPacket->head.psn = CmdPacketRxInfo.tsn;
    pAckPacket->ret = res;
    pAckPacket->head.crc16 = CRC16_XMODEM((uint8_t *)&(pAckPacket->head.cmd), pAckPacket->head.len);
    uint16_t send_len = sizeof(AckPacket_stu_t);
    Device_Write(XUNMEI_UART_VHWL, buffer, send_len, 0);
    PrintfHexData(buffer, send_len, "TX ACK PACK");
}

/**
  * @brief  处理接收的命令包
  * @note
  *
  * @param  pCmdPacket：数据包指针
  */
static void ProcessRxCmdPacket(CmdPacket_stu_t *pCmdPacket, uint16_t datalen)
{
    XM_LOG("rx cmd pack: 0x%02x, psn: 0x%02x, tsn: 0x%02x\r\n", pCmdPacket->head.cmd, pCmdPacket->head.psn, CmdPacketRxInfo.tsn);
    if (pCmdPacket->head.psn == CmdPacketRxInfo.tsn && pCmdPacket->head.psn && CmdPacketRxInfo.tsn) //相同包
    {
        XM_LOG("same sn\r\n");
        SendAckPacket(pCmdPacket->head.cmd, 1);
        return;
    }

    CmdPacketRxInfo.tsn = pCmdPacket->head.psn;

    /* 处理特殊命令帧 */
    if (pCmdPacket->head.cmd == RX_CMD_MULTI_PACKET || pCmdPacket->head.cmd == RX_CMD_MULTI_PACKET_1) //多包接收
    {
        MultiCmdPacket_stu_t *multi_packet = (MultiCmdPacket_stu_t *)pCmdPacket;
        uint16_t total_len = multi_packet->data.data_len;
        uint16_t cur_len = multi_packet->head.len - 5;

        if (multi_packet->data.pack_id == 0) //第一帧
        {
            if (CmdPacketRxInfo.muti_data) //上一个多帧包未接收完整，这里清除
            {
                OSAL_Free(CmdPacketRxInfo.muti_data);
                CmdPacketRxInfo.muti_data = NULL;
            }
            CmdPacketRxInfo.muti_data = OSAL_Malloc(total_len + 1); //预留结束符
            CmdPacketRxInfo.muti_data[total_len] = '\0';
            CmdPacketRxInfo.received_len = 0;

            if (CmdPacketRxInfo.muti_data == NULL) //内存不够丢弃当前帧数据
            {
                XM_LOG("memory err\r\n");
                return;
            }
        }

        if (CmdPacketRxInfo.muti_data == NULL) //第一帧丢失
        {
            XM_LOG("loss frame\r\n");
            return;
        }

        /* 缓存数据及节点位置 */
        memcpy(&CmdPacketRxInfo.muti_data[CmdPacketRxInfo.received_len], multi_packet->data.dataBuf, cur_len);
        CmdPacketRxInfo.received_len += cur_len;

        XM_LOG("rx multi-pack: %d / %d\r\n", CmdPacketRxInfo.received_len, total_len);

        /* 接收数据结束，转发数据到应用层 */
        if (CmdPacketRxInfo.received_len >= total_len)
        {
            XunMei_MsgPublish(XM_RECV_CMD_PACKET, NULL, UART_SEND_MAX, pCmdPacket->head.cmd, total_len + 1, CmdPacketRxInfo.muti_data);
            CmdPacketRxInfo.received_len = 0;
            OSAL_Free(CmdPacketRxInfo.muti_data);
            CmdPacketRxInfo.muti_data = NULL;
        }
        else
        {
            /* 数据未接收完，直接回复应答帧 */
            uint8_t res = XUNMEI_EXE_OK;
            XunmeiSend_Cmd(pCmdPacket->head.cmd, &res, sizeof(res), NULL, COMMAND_ACK);
        }
        return;
    }

    /* 收到了唤醒通知0x20 */
    if (pCmdPacket->head.cmd == TX_RX_CMD_WAKE_NOTIFI)
    {
        if (CmdPacketTxInfo.status == TX_STA_WAIT_NOTIFY)
        {
            XM_LOG("send cmd pack: %02X\r\n", ((CmdPacket_stu_t *)CmdPacketTxInfo.data->buffer)->head.cmd);
            CmdPacketTxInfo.status = TX_STA_IDLE; //切换到空闲状态
            OSAL_SetTaskStatus(TASK_STA_NORMAL);
            XunMei_SendData(CmdPacketTxInfo.data->buffer, CmdPacketTxInfo.data->len);
            CmdPacketTxInfo.status = TX_STA_WAIT_ACK; //切换到等待ACK状态
            CmdPacketTxInfo.timeStamp = OSAL_GetTickCount();
        }
    }

    /* 数据转发到应用层 */
    XunMei_MsgPublish(XM_RECV_CMD_PACKET, NULL, UART_SEND_MAX, pCmdPacket->head.cmd, pCmdPacket->head.len - 2, pCmdPacket->dataBuf);
}

/**
  * @brief  解析接收的数据包
  * @note
  *
  * @param  pData：数据包地址
  */
static void XunMei_ParseRxPacket(uint8_t *pData, uint16_t datalen)
{
    if (pData == NULL)
    {
        return;
    }

    ProtocolDataHead_stu_t *pPacketHead = (ProtocolDataHead_stu_t *)pData;

    CmdPacketRxInfo.timeStamp = OSAL_GetTickCount(); // 更新RX时间戳
    if (pPacketHead->stx == CMD_PACKET_STX)          //命令包
    {
        ProcessRxCmdPacket((CmdPacket_stu_t *)pData, datalen);
    }
    else //应答包
    {
        ProcessRxAckPacket((AckPacket_stu_t *)pData, datalen);
    }
}

/**
  * @brief  从底层环形缓存获取一包数据并解析
  * @note   底层驱动收到数据，会暂存在环形缓存区
  * @param  out_data:解析后数据
  * @return 返回解析后的数据包长度，解析失败返回0
  */
static uint16_t XunMei_GetOnePacket(uint8_t *out_data)
{
    if (out_data == NULL)
        return 0;

    __EFRAM static uint8_t getPacketTimeoutCnt;
    uint16_t sum = 0, packetLen = 0;
    uint8_t tmpData;
    uint32_t tmpLen = 0;
    uint8_t *protocolBuff;
    __EFRAM static uint16_t Cnt = 0;
    ProtocolDataHead_stu_t *pPacketHead;

    tmpLen = OSAL_RingBufferGetValidSize(CmdPacketRxInfo.Rx_rbHandle);
    if (tmpLen == 0)
    {
        if (++getPacketTimeoutCnt >= 3)
        {
            getPacketTimeoutCnt = 0;
            Cnt = 0;
        }
        return 0;
    }

    getPacketTimeoutCnt = 0;
    protocolBuff = out_data;
    for (; tmpLen; tmpLen--)
    {
        OSAL_RingBufferRead(CmdPacketRxInfo.Rx_rbHandle, &tmpData);
        if (Cnt == 0)
        {
            if (tmpData == CMD_PACKET_STX || tmpData == ACK_PACKET_STX)
            {
                protocolBuff[0] = tmpData;
                Cnt = 1;
            }
        }
        else if (Cnt > 0)
        {
            protocolBuff[Cnt] = tmpData;
            Cnt = Cnt + 1;
            if (Cnt > 5)
            {
                pPacketHead = (ProtocolDataHead_stu_t *)protocolBuff;
                packetLen = pPacketHead->len + 5;
                if (Cnt >= MAX_PACKAGE_LEN_XM || packetLen == Cnt)
                {
                    if (packetLen == Cnt)
                    {
                        sum = CRC16_XMODEM(protocolBuff + 5, pPacketHead->len);
                        if (pPacketHead->crc16 == sum)
                        {
                            if (pPacketHead->cmd != 0x11) //不是下发的JSON包数据
                            {
                                PrintfHexData(protocolBuff, Cnt, "RX DATA");
                            }
                            else
                            {
                                PrintfHexData(protocolBuff, sizeof(MultiCmdPacket_stu_t), "RX JSON-PACK");
                            }
                            uint16_t data_len = Cnt;
                            Cnt = 0;
                            return data_len;
                        }
                        else
                        {
                            PrintfHexData(protocolBuff, Cnt, "CRC ERR");
                        }
                    }
                    Cnt = 0;
                    return 0;
                }
            }
        }
    }
    return 0;
}

/**
  * @brief  串口接收回调函数
  * @note
  * @param  data:收到的数据
  * @param  len:数据长度
  * @return
  */
static void XunMei_ReceiveCb(VirtualHardware_enum_t dev, void *data, uint32_t len)
{
    if (CmdPacketRxInfo.Rx_rbHandle == NULL)
    {
        return;
    }
    for (uint32_t i = 0; i < len; i++)
    {
        uint8_t tmp = ((uint8_t *)data)[i];
        if (OSAL_RingBufferWrite(CmdPacketRxInfo.Rx_rbHandle, &tmp) != SUCCESS)
        {
            return;
        }
    }
}

/**
  * @brief  唤醒脚中断回调函数
  * @note
  * @param  name：中断脚名字
  * @param  status:状态
  * @param  times:
  * @return
  */
static void XunMei_WakeStatusChange(char *name, uint8_t status, uint8_t times)
{
    if (status == USER_ACTION_PRESS)
    {
        OSAL_EventCreateFromISR(COMP_XUNMEI);
    }
}

/**
  * @brief  协议层初始化
  *
  * @note   创建缓存、注册中断
  */
static void XunMei_ProtocolInit(void)
{
    if (xunmei_init_flag == 0)
    {
        xunmei_init_flag = UINT32_FLAG;
        CmdPacketTxInfo.status = TX_STA_IDLE;
        CmdPacketTxInfo.tsn = 0;
        CmdPacketTxInfo.queue_tx_handle = OSAL_QueueCreate(TX_QUEUE_LEN);
        CmdPacketRxInfo.Rx_rbHandle = OSAL_RingBufferCreate(UART_REV_DATA_BUF_LEN, 1);
        CmdPacketRxInfo.received_len = 0;
        CmdPacketRxInfo.muti_data = NULL;

        Device_RegisteredCB(XUNMEI_UART_VHWL, XunMei_ReceiveCb);

        InputPort_stu_t button_list[] = {
            {"XUNMEI_WAKE", XUNMEI_WAKE_PIN_VHWL, INPUT_PORT_LOGIC_LOW, INPUT_PORT_FUNC_SINGLE}, //中断脚
        };

        /* 注册端口 */
        InputPort_Registered(button_list, OSAL_LENGTH(button_list), XunMei_WakeStatusChange);
        InputPort_EnableProt("XUNMEI_WAKE");
    }
}

/**
  * @brief  立即发送
  * @note
  * @param  data:需要发送的数据包
  * @return 0：成功，其他失败
  */
static int XunmeiSend_Now(uart_txcmd_packet_stu_t *data)
{
    if (data == NULL)
    {
        return -1;
    }

    XM_LOG("send cmd pack: 0x%02x\r\n", ((CmdPacket_stu_t *)data->buffer)->head.cmd);
    OSAL_SetTaskStatus(TASK_STA_ACTIVE);

    CmdPacketTxInfo.data = data;
    CmdPacketTxInfo.cmd = ((CmdPacket_stu_t *)CmdPacketTxInfo.data->buffer)->head.cmd;
    CmdPacketTxInfo.tsn = ((CmdPacket_stu_t *)CmdPacketTxInfo.data->buffer)->head.psn;
    CmdPacketTxInfo.cnt = 1;
    CmdPacketTxInfo.wake_cnt = 1;

    uint32_t currentTime = OSAL_GetTickCount();
    if (OSAL_PastTime(currentTime, CmdPacketRxInfo.timeStamp) < PROTOCOL_XM_SLEEP_TIME && CmdPacketRxInfo.timeStamp != 0) //模组活跃
    {
        XunMei_SendData(CmdPacketTxInfo.data->buffer, CmdPacketTxInfo.data->len);
        CmdPacketTxInfo.timeStamp = OSAL_GetTickCount();
        CmdPacketTxInfo.status = TX_STA_WAIT_ACK; //等待模块回复应答包
    }
    else
    {
        XunMei_SendWakeSignal();                         //拉wake
        CmdPacketTxInfo.timeStamp = OSAL_GetTickCount(); //刷新时间戳
        CmdPacketTxInfo.status = TX_STA_WAIT_NOTIFY;     //等待唤醒通知
    }
    return 0;
}

/**
  * @brief  处理发送队列中的数据
  * @note
  * @return
  */
static void XunMei_ProcessTxQueue(void)
{
    if (xm_ready > 0)
    {
        xm_ready--; //讯美模组刚上电，还未就绪
        return;
    }

    if (CmdPacketTxInfo.status != TX_STA_IDLE)
    {
        return;
    }

    uint16_t len = 0;
    if (OSAL_QueueLenght(CmdPacketTxInfo.queue_tx_handle, &len))
    {
        uart_txcmd_packet_stu_t *txcmd_packet = (uart_txcmd_packet_stu_t *)OSAL_Malloc(len);
        if (txcmd_packet && OSAL_QueueReceive(CmdPacketTxInfo.queue_tx_handle, (void *)txcmd_packet))
        {
            XunmeiSend_Now(txcmd_packet);
        }
    }
}

/**
  * @brief  Uart任务 通信协议处理
  * @note   该函数会每间隔10ms执行一次
  */
static void XunMei_ProtocolPro(void)
{
    uint16_t len = XunMei_GetOnePacket(CmdPacketRxInfo.uart_rev_data);
    if (len > 0)
    {
        err_process(0);
        XunMei_ParseRxPacket(CmdPacketRxInfo.uart_rev_data, len); //解析数据
    }
    XunMei_ProcessTimeoutResend(); //处理超时重发
    XunMei_ProcessTxQueue();
}

/**
  * @brief  发送命令包
  * @note
  * @param  cmd：需要发送的命令
  * @param  pdata：命令数据
  * @param  len：数据长度
  * @param  cb：发送回调
  * @param  pack_type：包类型
  * @return int
  */
static int XunmeiSend_Cmd_1(uint8_t cmd, uint8_t *pdata, uint16_t len, uart_send_cb_fun_t cb, cmd_packet_type pack_type)
{
    if (Device_Read(XUNMEI_CTRL_PIN_VHWL, NULL, 0, 0) == 1)
    {
        __XM_LOG("xm power off, cmd: %02X\r\n", cmd);
        return -3;
    }

    if (pack_type == COMMAND_ACK) //发送ACK
    {
        if (pdata != NULL)
        {
            SendAckPacket(cmd, *pdata);
        }
        return 0;
    }

    uint16_t queue_msg_len = sizeof(uart_txcmd_packet_stu_t) + sizeof(CmdPacket_stu_t) + len;
    uart_txcmd_packet_stu_t *txcmd_packet = (uart_txcmd_packet_stu_t *)OSAL_Malloc(queue_msg_len);
    if (txcmd_packet == NULL)
    {
        return -1;
    }

    __EFRAM static uint8_t tsn = 0;
    CmdPacket_stu_t *p_buffer = (CmdPacket_stu_t *)txcmd_packet->buffer;
    p_buffer->head.stx = CMD_PACKET_STX;
    p_buffer->head.len = len + 2; //有效数据域长度+cmd
    p_buffer->head.cmd = cmd;
    p_buffer->head.psn = tsn;
    if (pdata && len)
    {
        memcpy(p_buffer->dataBuf, pdata, len);
    }
    p_buffer->head.crc16 = CRC16_XMODEM(&(p_buffer->head.cmd), len + 2);
    txcmd_packet->len = sizeof(CmdPacket_stu_t) + len; //整个包数据长度
    txcmd_packet->cb = cb;
    tsn++;

    if (cmd == TX_RX_CMD_WAKE_NOTIFI)
    {
        XunMei_SendData((uint8_t *)p_buffer, sizeof(CmdPacket_stu_t) + len);
        OSAL_Free(txcmd_packet);
        return 0;
    }

    if (CmdPacketTxInfo.status == TX_STA_IDLE && xm_ready == 0) //发送状态为空闲，直接发送
    {
        return XunmeiSend_Now(txcmd_packet);
    }
    else //通信繁忙，暂存队列中
    {
        if (CmdPacketTxInfo.queue_tx_handle == NULL)
        {
            return -1;
        }  
        XM_LOG("queue write(0x%02x, %d)\r\n", ((CmdPacket_stu_t *)txcmd_packet->buffer)->head.cmd, queue_msg_len);
        if (OSAL_QueueSend(CmdPacketTxInfo.queue_tx_handle, (void *)txcmd_packet, queue_msg_len, RESET) != SUCCESS)
        {
            OSAL_Free(txcmd_packet);
            return -2;
        }
        __XM_LOG("qlen: %d\r\n", OSAL_QueueLenght(CmdPacketTxInfo.queue_tx_handle, &queue_msg_len));
        OSAL_Free(txcmd_packet);
    }
    return 0;
}

/**
  * @brief  发送命令包
  * @note   会对多包命令拆包发送
  * @param  cmd：需要发送的命令
  * @param  pdata：命令数据
  * @param  len：数据长度
  * @param  cb：发送回调
  * @param  pack_type：包类型
  * @return int
  */
static int XunmeiSend_Cmd(uint8_t cmd, uint8_t *pdata, uint16_t len, uart_send_cb_fun_t cb, cmd_packet_type pack_type)
{
    if (cmd == TX_CMD_MULTI_PACKET) //多包发送命令
    {
        MultiCmdPacketData_stu_t *send_data = (MultiCmdPacketData_stu_t *)OSAL_Malloc(MAX_PACKAGE_LEN_XM - sizeof(ProtocolDataHead_stu_t));
        if (send_data == NULL)
        {
            __XM_LOG("xm malloc error\r\n");
            return -1;
        }

        uint16_t remain_len = len, sent_len = 0;
        uint16_t cur_len = 0;
        uint16_t max_data_len = MAX_PACKAGE_LEN_XM - sizeof(MultiCmdPacket_stu_t);
        send_data->data_len = len;
        send_data->pack_id = 0;
        while (remain_len > 0)
        {
            cur_len = (remain_len > max_data_len) ? max_data_len : remain_len; //发送的json长度
            memcpy(send_data->dataBuf, &pdata[sent_len], cur_len);
            if (XunmeiSend_Cmd_1(cmd, (uint8_t *)send_data, cur_len + sizeof(MultiCmdPacketData_stu_t), cb, pack_type) != 0)
            {
                __XM_LOG("xm send err\r\n");
                OSAL_Free(send_data);
                return -2;
            }
            sent_len += cur_len;
            remain_len -= cur_len;
            send_data->pack_id++;
            XM_LOG("send multi-pack: %d / %d\r\n", sent_len, len);
        }
        OSAL_Free(send_data);
    }
    else
    {
        return XunmeiSend_Cmd_1(cmd, pdata, len, cb, pack_type);
    }
    return 0;
}

/**
  * @brief  判断协议层是否为空闲状态
  * @note
  *
  * @return 空闲：返回SUCCESS， 正忙：返回ERROR
  */
static ErrorStatus XunMei_IsProtocolIdle(void)
{
    /* 当前没有发出命令、发送缓存为空、接收的命令也处理完成了，才能发出新命令 */
    if (CmdPacketTxInfo.status == TX_STA_IDLE)
    {
        return SUCCESS;
    }
    return ERROR;
}

/**
  * @brief  打开关闭讯美模块电源
  * @note
  * @param  onoff:SET，开 ; RESET，关
  * @return
  */
static int XunMei_Power(FlagStatus onoff)
{
    __XM_LOG("xm power: %d\r\n", onoff);
    if (onoff == SET)
    {
        if (Device_Read(XUNMEI_CTRL_PIN_VHWL, NULL, 0, 0) == 1)
        {
            /* 讯美从断电到上电：改成1s后再与之交互 */
            xm_ready = XUNMEI_STARTUP_TIME; //标记讯美需要150*10ms后，才允许发出数据
        }

        Device_Enable(XUNMEI_WAKE_PIN_VHWL);
        Device_Enable(XUNMEI_UART_VHWL);
        Device_Write(XUNMEI_CTRL_PIN_VHWL, NULL, 0, 0);

        /* 中断IO设置成数字高阻，开启中断 */
        Device_Write(XUNMEI_WAKE_PIN_VHWL, NULL, 0, PIN_MODE_INPUT_HIGHZ);
        InputPort_EnableProt("XUNMEI_WAKE");
    }
    else
    {
        Device_Disable(XUNMEI_WAKE_PIN_VHWL);
        Device_Disable(XUNMEI_UART_VHWL);
        Device_Write(XUNMEI_CTRL_PIN_VHWL, NULL, 0, 1);

        /* 中断IO设置成下拉低，禁用中断 */
        InputPort_DisableProt("XUNMEI_WAKE");
        Device_Write(XUNMEI_WAKE_PIN_VHWL, NULL, 0, PIN_MODE_INPUT_PULLDOWN);
    }
    return 0;
}

/**
  * @brief  讯美模块任务函数
  * @note
  * @param  event：当前任务的所有事件
  * @return 返回未处理的事件
  */
static uint32_t XunMei_Task(uint32_t event)
{
    /* 系统启动事件 */
    if (event & EVENT_SYS_START)
    {
        XunMei_ProtocolInit();
        OSAL_EventRepeatCreate(COMP_XUNMEI, EVENT_XUNMEI_PROTOCOL, XUNMEI_PROTOCOL_PROC_CYCLE, EVT_PRIORITY_MEDIUM);
        SYS_API(XunmeiSend_Cmd);
        SYS_API(XunMei_SendWakeSignal);
        SYS_API(XunMei_IsProtocolIdle);
        SYS_API(XunMei_Power);

        if (xm_wake_flag != 0)
        {
            xm_wake_flag = 0;
            OSAL_EventCreateFromISR(COMP_XUNMEI);
        }
        return (event ^ EVENT_SYS_START);
    }

    if (event & EVENT_SYS_SLEEP)
    {
        Device_Disable(XUNMEI_UART_VHWL);
        OSAL_EventDelete(COMP_XUNMEI, EVENT_XUNMEI_PROTOCOL);
        return (event ^ EVENT_SYS_SLEEP);
    }

    if (event & EVENT_XUNMEI_PROTOCOL)
    {
        XunMei_ProtocolPro();
        return (event ^ EVENT_XUNMEI_PROTOCOL);
    }

    if (event & EVENT_SYS_ISR)
    {
        __XM_LOG("xm isr\r\n");
        CmdPacketRxInfo.timeStamp = OSAL_GetTickCount();               //更新RX时间戳
        XunmeiSend_Cmd(TX_RX_CMD_WAKE_NOTIFI, NULL, 0, NULL, COMMAND); //唤醒通知
        return (event ^ EVENT_SYS_ISR);
    }
    return 0;
}
COMPONENT_TASK_EXPORT(COMP_XUNMEI, XunMei_Task, 0);

/**
  * @brief  唤醒逻辑处理
  *         当vPIN触发中断时，OS会调用该函数确认是否可以唤醒系统
  */
static int32_t XunMei_WakeupHandle(uint32_t dev)
{
    /* RAM数据未丢失，inputport会唤醒系统，这里wakehandle不用做任何处理 */
    if (xunmei_init_flag == UINT32_FLAG)
    {
        return -1;
    }

    /* RAM数据丢失了，PIN设备唤醒，要做消抖判断 */
    xm_wake_flag = 1;
    return 1; // TODO
}
COMPONENT_WAKEUP_EXPORT(COMP_XUNMEI, XunMei_WakeupHandle, XUNMEI_WAKE_PIN_VHWL);
